#include "stdafx.h"
#include <winsock2.h>
#include "HXEvents.h"
#include "HXPlayer.h"
#include "HXWindow.h"

extern "C" {
#include "th_api.h"
}

//=============================================================================

HXEventClient g_THEvents;			// Thinking Head event client

//=============================================================================


HXEventClient::HXEventClient()
: m_hThread(0),
  m_bThreadExit(false),
  m_bConnected(false),
  m_bSend(false),
  m_bReceive(false),
  m_hMenu(0),
  m_itemId(0),
  m_sendId(0), 
  m_recvId(0),
  m_renderComplete(0),
  m_hWnd(0)
{
	InitializeCriticalSectionAndSpinCount(&m_criticalSection_Event, 0x80000400);
	m_eventCfgFname = string(TH_CONFIG_FILENAME);
}

HXEventClient::~HXEventClient()
{
	Disconnect();
	DeleteCriticalSection(&m_criticalSection_Event);
}

void HXEventClient::SetMenu(HMENU hMenu, int itemId, int sendId, int recvId) 
{ 
	m_hMenu = hMenu; 
	m_itemId = itemId; 
	m_sendId = sendId; 
	m_recvId = recvId; 
}

void HXEventClient::Connect()
{
	Disconnect();
	if (m_hMenu)
		CheckMenuItem(m_hMenu, m_itemId, MF_CHECKED);
	m_bConnected = true;
	DWORD threadID;
	m_hThread = CreateThread( 
                     NULL,       // default security attributes
                     0,          // default stack size
					 (LPTHREAD_START_ROUTINE) RunClientThread, 
                     this,		 // object pointer
                     0,          // default creation flags
                     &threadID); // receive thread identifier
}

void HXEventClient::Disconnect()
{
	if (m_hThread)
	{
		m_bThreadExit = true;
		WaitForSingleObject(m_hThread, INFINITE);
		CloseHandle(m_hThread);
		m_bThreadExit = false;
		m_hThread = 0;
	}
}

DWORD WINAPI HXEventClient::RunClientThread(LPVOID lpParam) 
{ 
	return ((HXEventClient*)lpParam)->ClientThread();
}

DWORD WINAPI HXEventClient::ClientThread()
{
	char *pClientName = "Head Synthesis";
	char *pClientVersion = "Head X";
	wchar_t currentPath[MAX_PATH];
	GetCurrentDirectory(MAX_PATH, currentPath);
	g_paths.SetWorkingPath(wstring(g_paths.GetStartupPath())); // Synapse share...
	g_window.ShowStatus(L"Connecting to Event Manager...");
	THHandle *pEvtMgr = TH_Connect(pClientName, pClientVersion); // connect to the event manager
	g_paths.SetWorkingPath(wstring(currentPath)); // Synapse share...
	if (pEvtMgr)
	{
		THLog_Entry(pEvtMgr, "Head X Synthesis Engine connected");

		EnableMenuItem(m_hMenu, m_sendId, MF_ENABLED);
		EnableMenuItem(m_hMenu, m_recvId, MF_ENABLED);
		

		THEvent_SubscribeType(pEvtMgr, TH_EVENT_DM_RESPONSE);
		THEvent_SubscribeType(pEvtMgr, TH_EVENT_EMOTE);

		if (TH_IsConnected(pEvtMgr))
			g_window.ShowStatus(L"Ready to process events...");

		while (!m_bThreadExit) 
		{			
			if (m_bSend)
			{
				EnterCriticalSection(&m_criticalSection_Event);
				if (!m_personSpokeBuffer.empty())
				{
					THEvent *pNEvt = THEvent_InitEvent(TH_EVENT_PERSON_SPOKE);
					THEvent_SetTag(pNEvt, NULL, TAG_SPEECH, const_cast<char*>(m_personSpokeBuffer.c_str()));
					THEvent_SetEvent(pEvtMgr, pNEvt);
					m_personSpokeBuffer.clear();
					LeaveCriticalSection(&m_criticalSection_Event);
				}
				else if (m_renderComplete > 0)
				{
					THEvent *pNEvt = THEvent_InitEvent(TH_EVENT_RENDER_COMPLETE);
					THEvent_SetEvent(pEvtMgr, pNEvt);
					m_renderComplete = 0;
					LeaveCriticalSection(&m_criticalSection_Event);
				}
				else
				{
					LeaveCriticalSection(&m_criticalSection_Event);
					if (!m_bReceive)
						Sleep(EVENT_POLL_SLEEP);
				}
			}
			if (!m_bReceive)
			{
				if (!m_bSend)
					Sleep(EVENT_POLL_SLEEP);
				continue;
			}
			uint32 timeout = 0;
			int iResult = THEvent_CheckEvents(pEvtMgr, timeout);
			if (iResult)
			{
				g_window.ShowErrorStatus(L"Connection to Event Manager failed");
				break;
			}
			else
			{
				THEvent *pEvt = THEvent_ReadEvent(pEvtMgr);
				if (!pEvt && m_waitlist.size() /*&& !myHead->isStillPlaying()*/)
				{
					// play a queued speech event
					pEvt = static_cast<THEvent*>(m_waitlist.front());
					m_waitlist.pop_front();
				}
				if (pEvt)
				{
					switch(pEvt->hdr.type)
					{
						case TH_EVENT_DM_RESPONSE:
						{
							
							char *pStopSpeech = THEvent_GetTag(pEvt, pEvt->xmlroot, TAG_STOPSPEECH);
							char *pUserSpeech = THEvent_GetTag(pEvt, pEvt->xmlroot, TAG_SPEECH);
									
							if (pStopSpeech)
							{
								char buf[500];
								sprintf_s(buf, 500, "silence command");
								dbg(THDbgLevel_Critical, buf);
								g_window.ShowStatus(L"DM response: (silence)");
								AsyncSpeak(wstring(L""));

								// discard any queued speech requests
								while (m_waitlist.size())
									m_waitlist.pop_front();
							}
							else 
							if (pUserSpeech)
							{
								// say it now, or say it when the current utterance has completed?
								char *pWhen = THEvent_GetTag(pEvt, pEvt->xmlroot, TAG_SAYWHEN); 
								int now = 0;
								if (pWhen && !_stricmp(pWhen, "now")) // say it now
								{
									now = 1;

									// discard any queued speech requests
									while (m_waitlist.size())
										m_waitlist.pop_front();
								}
								/*if (myHead->isStillPlaying() && !now)
								{
									if (m_waitlist.size() < MAX_SPEECH_QUEUE)
									{
										// just queue the request for later
										m_waitlist.push_back(pEvt);
									}
								}
								else*/
								{
									//char *pId = THEvent_GetTag(pEvt, pEvt->xmlroot, TAG_ID);
									char buf[1000];
									sprintf_s(buf, sizeof(buf), "%s", pUserSpeech);
									dbg(THDbgLevel_Critical, buf);
									/*if (pId)
										myHead->setCurrentId(pId);									
									else
										myHead->setCurrentId("unknown_ID");*/
											
									/*speech_in_progress = 1;*/
									/*myHead->makeHeadAnswer(user_speech);*/
									g_window.ShowStatus(L"DM response: \"%s\"", StringToWString(string(pUserSpeech)).c_str());
									AsyncSpeak(StringToWString(string(pUserSpeech)));
								}
							}
							break;
						}
						case TH_EVENT_EMOTE:
						{
							char *pEmote = THEvent_GetTag(pEvt, pEvt->xmlroot, TAG_EMOTE);
							if (pEmote)
							{
								/* --- */
							}
							break;
						}
						case TH_EVENT_KILL:
						{
							g_window.ShowStatus(L"Received KILL event, exiting...");
							PostMessage(m_hWnd, WM_CLOSE, 0, 0);
						}
					} // switch
				} // if pEvt
			} // else iResult
			Sleep(EVENT_POLL_SLEEP);
		} // while
		TH_Disconnect(pEvtMgr);
		pEvtMgr = NULL;
	} // if pEvtMgr
	else
	{
		g_window.ShowErrorStatus(L"Event Manager not found");
	}
	m_bConnected = false;
	if (m_hMenu)
	{
		CheckMenuItem(m_hMenu, m_itemId, MF_UNCHECKED);
		EnableMenuItem(m_hMenu, m_sendId, MF_GRAYED);
		EnableMenuItem(m_hMenu, m_recvId, MF_GRAYED);
	}
	g_window.ShowStatus(L"Stopped processing events");
	return 0;
}

void HXEventClient::SendPersonSpoke(const string& sps)
{
	EnterCriticalSection(&m_criticalSection_Event);
	m_personSpokeBuffer = sps;
	LeaveCriticalSection(&m_criticalSection_Event);
	g_window.ShowStatus(L"Sending PERSON_SPOKE event: \"%s\"", StringToWString(sps).c_str());
}

void HXEventClient::SendRenderComplete()
{
	EnterCriticalSection(&m_criticalSection_Event);
	m_renderComplete++;
	LeaveCriticalSection(&m_criticalSection_Event);
	g_window.ShowStatus(L"Sending RENDER_COMPLETE");
}